#include <cstring>
#include <cstdio>

#include "variant.h"

data_chunk::data_chunk():   size( 0 ),
                            data( NULL )
{}
data_chunk::data_chunk(unsigned char* dta, unsigned int sze){
    size = sze;
    data = new unsigned char[size];
    memcpy(data,dta,size);
}
data_chunk::data_chunk(data_chunk* chunk){
    size = chunk->size;
    data = new unsigned char[size];
    memcpy(data,chunk->data,size);
}
data_chunk::~data_chunk(){
    if( data ) delete[] data;
}
void data_chunk::copy( data_chunk* source ){
    if( data ) delete[] data;
    size = source->size;
    data = new unsigned char[size];
    memcpy(data,source->data,size);
}

void data_chunk::exportDC( FILE* file ){

    fwrite( &size, sizeof( unsigned int ), 1, file );
    fwrite( data, size, 1, file );
    fflush( file );

}
void data_chunk::importDC( FILE* file ){

    fread( &size, sizeof( unsigned int ), 1, file );
    data = new unsigned char[size];
    fread( data, size, 1, file );

}

variant::variant(): byType( VAR_UNDEF )
{
        uVal.uiVal = 0;
}
variant::~variant(){
    clear();
}

void variant::copy(variant* var){
    clear();
    switch( var->getType() ){
        case VAR_UINT:      setUInt(var->getUInt());        break;
        case VAR_INT:       setInt(var->getInt());          break;
        case VAR_USHORT:    setUShort(var->getUShort());    break;
        case VAR_SHORT:     setShort(var->getShort());      break;
        case VAR_ULONG:     setULong(var->getULong());      break;
        case VAR_LONG:      setLong(var->getLong());        break;
        case VAR_BYTE:      setByte(var->getByte());        break;
        case VAR_CHAR:      setChar(var->getChar());        break;
        case VAR_FLOAT:     setFloat(var->getFloat());      break;
        case VAR_DOUBLE:    setDouble(var->getDouble());    break;
        case VAR_STRING:    setString(var->getString());    break;
        case VAR_DATA:      setData(var->getData());        break;
    }
}

void variant::exportVariant(FILE* file){

    fwrite( &byType, sizeof( unsigned char ), 1, file );

    switch( byType ){
        case VAR_UINT:      fwrite( &uVal, sizeof(unsigned int), 1, file );     break;
        case VAR_INT:       fwrite( &uVal, sizeof(int), 1, file );              break;
        case VAR_USHORT:    fwrite( &uVal, sizeof(unsigned short), 1, file );   break;
        case VAR_SHORT:     fwrite( &uVal, sizeof(short), 1, file );            break;
        case VAR_ULONG:     fwrite( &uVal, sizeof(unsigned long), 1, file );    break;
        case VAR_LONG:      fwrite( &uVal, sizeof(long), 1, file );             break;
        case VAR_BYTE:      fwrite( &uVal, sizeof(unsigned char), 1, file );    break;
        case VAR_CHAR:      fwrite( &uVal, sizeof(char), 1, file );             break;
        case VAR_FLOAT:     fwrite( &uVal, sizeof(float), 1, file );            break;
        case VAR_DOUBLE:    fwrite( &uVal, sizeof(double), 1, file );           break;
        case VAR_STRING:{
            size_t size = strlen( uVal.pszVal );
            fwrite( &size, sizeof( size_t ), 1, file );
            fwrite( uVal.pszVal, size, 1, file );
        }   break;
        case VAR_DATA:  uVal.chunkVal->exportDC( file );                 break;
    }

    fflush( file );

}
void variant::importVariant(FILE* file){

    clear();

    printf("Variant: bftell%ld\n",ftell(file));

    fread( &byType, sizeof( unsigned char ), 1, file );

    printf("Type detecte: %s\n",variant::typeToString(byType));

    switch( byType ){
        case VAR_UINT:      fread( &uVal, sizeof(unsigned int), 1, file );     printf("%u\n",uVal.uiVal);break;
        case VAR_INT:       fread( &uVal, sizeof(int), 1, file );              printf("%d\n",uVal.iVal);break;
        case VAR_USHORT:    fread( &uVal, sizeof(unsigned short), 1, file );   break;
        case VAR_SHORT:     fread( &uVal, sizeof(short), 1, file );            break;
        case VAR_ULONG:     fread( &uVal, sizeof(unsigned long), 1, file );    break;
        case VAR_LONG:      fread( &uVal, sizeof(long), 1, file );             break;
        case VAR_BYTE:      fread( &uVal, sizeof(unsigned char), 1, file );    printf("%hd\n",(unsigned short)(uVal.byVal));break;
        case VAR_CHAR:      fread( &uVal, sizeof(char), 1, file );             break;
        case VAR_FLOAT:     fread( &uVal, sizeof(float), 1, file );            break;
        case VAR_DOUBLE:    fread( &uVal, sizeof(double), 1, file );           break;
        case VAR_STRING:{
            size_t size;
            fread( &size, sizeof( size_t ), 1, file );
            uVal.pszVal = new char[size+1];
            uVal.pszVal[size] = 0x00;
            fread( uVal.pszVal, size, 1, file );
        }   break;
        case VAR_DATA:{
            uVal.chunkVal = new data_chunk();
            uVal.chunkVal->importDC( file );
        }   break;
    }
    printf("Variant: eftell%ld\n",ftell(file));

}

void variant::setUInt(unsigned int val){
    clear();
    byType = VAR_UINT;
    uVal.uiVal = val;
}
void variant::setInt(int val){
    clear();
    byType = VAR_INT;
    uVal.iVal = val;
}
void variant::setUShort(unsigned short val){
    clear();
    byType = VAR_USHORT;
    uVal.unVal = val;
}
void variant::setShort(short val){
    clear();
    byType = VAR_SHORT;
    uVal.nVal = val;
}
void variant::setULong(unsigned long val){
    clear();
    byType = VAR_ULONG;
    uVal.ulVal = val;
}
void variant::setLong(unsigned long val){
    clear();
    byType = VAR_LONG;
    uVal.lVal = val;
}
void variant::setByte(unsigned char val){
    clear();
    byType = VAR_BYTE;
    uVal.byVal = val;
}
void variant::setChar(char val){
    clear();
    byType = VAR_CHAR;
    uVal.cVal = val;
}
void variant::setFloat(float val){
    clear();
    byType = VAR_FLOAT;
    uVal.fVal = val;
}
void variant::setDouble(double val){
    clear();
    byType = VAR_DOUBLE;
    uVal.dVal = val;
}
void variant::setString(char* val){
    clear();
    printf("Instring:%s\n",val);
    byType = VAR_STRING;
    uVal.pszVal = new char[strlen(val) + 1];
    strcpy(uVal.pszVal,val);
    printf("Outstring:%s\n",uVal.pszVal);
}
void variant::setData(data_chunk* val){
    clear();
    byType = VAR_DATA;
    uVal.chunkVal = new data_chunk(val);
}
void variant::clear(){
    printf("clear\n");
    if( byType == VAR_UNDEF ) return;
    switch( byType ){
        case VAR_STRING:    delete[] uVal.pszVal;   break;
        case VAR_DATA:      delete uVal.chunkVal;   break;
    }
    byType = VAR_UNDEF;
}

unsigned char variant::getType(){
    return byType;
}

unsigned int variant::getUInt(){
    if( byType != VAR_UINT ) return 0;
    return uVal.uiVal;
}
int variant::getInt(){
    if( byType != VAR_INT ) return 0;
    return uVal.iVal;
}
unsigned short variant::getUShort(){
    if( byType != VAR_USHORT ) return 0;
    return uVal.unVal;
}
short variant::getShort(){
    if( byType != VAR_SHORT ) return 0;
    return uVal.nVal;
}
unsigned long variant::getULong(){
    if( byType != VAR_ULONG ) return 0;
    return uVal.ulVal;
}
long variant::getLong(){
    if( byType != VAR_LONG ) return 0;
    return uVal.lVal;
}
unsigned char variant::getByte(){
    if( byType != VAR_BYTE ) return 0;
    return uVal.byVal;
}
char variant::getChar(){
    if( byType != VAR_CHAR ) return 0;
    return uVal.cVal;
}
float variant::getFloat(){
    if( byType != VAR_FLOAT ) return 0;
    return uVal.fVal;
}
double variant::getDouble(){
    if( byType != VAR_DOUBLE ) return 0;
    return uVal.dVal;
}
char* variant::getString(){
    if( byType != VAR_STRING ) return NULL;
    return uVal.pszVal;
}
void variant::getString(char* str){
    if( byType != VAR_STRING ) return;
    strcpy(str,uVal.pszVal);
}
unsigned int variant::getStringLen(){
    if( byType != VAR_STRING ) return 0;
    return strlen(uVal.pszVal);
}
data_chunk* variant::getData(){
    if( byType != VAR_DATA ) return NULL;
    return uVal.chunkVal;
}
void variant::getData(data_chunk* var){
    if( byType != VAR_DATA ) return;
    var->copy(uVal.chunkVal);
}

const char* variant::typeToString(unsigned char type){
    switch( type ){
        case VAR_UINT:      return "Unsigned Int";
        case VAR_INT:       return "Int";
        case VAR_USHORT:    return "Unsigned Short";
        case VAR_SHORT:     return "Short";
        case VAR_ULONG:     return "Unsigned Long";
        case VAR_LONG:      return "Long";
        case VAR_BYTE:      return "Byte";
        case VAR_CHAR:      return "Char";
        case VAR_FLOAT:     return "Float";
        case VAR_DOUBLE:    return "Double";
        case VAR_STRING:    return "String";
        case VAR_DATA:      return "Data Chunk";
        default:            return "Undefined";
    }
}
const char* variant::typeToShortString(unsigned char type){
    switch( type ){
        case VAR_UINT:      return "uint";
        case VAR_INT:       return "int";
        case VAR_USHORT:    return "ush";
        case VAR_SHORT:     return "sh";
        case VAR_ULONG:     return "ulong";
        case VAR_LONG:      return "long";
        case VAR_BYTE:      return "byte";
        case VAR_CHAR:      return "char";
        case VAR_FLOAT:     return "float";
        case VAR_DOUBLE:    return "double";
        case VAR_STRING:    return "string";
        case VAR_DATA:      return "dhk";
        default:            return "undef";
    }
}

void variant::fromStrings(char* typeStr,char* valStr){
    if( !strcmp( typeStr, "uint" ) ){
        unsigned int var;
        if( valStr ){
            sscanf(valStr,"%u",&var);
            setUInt(var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "int" ) ){
        int var;
        if( valStr ){
            sscanf(valStr,"%d",&var);
            setInt(var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "ush" ) ){
        unsigned short var;
        if( valStr ){
            sscanf(valStr,"%hu",&var);
            setUShort(var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "sh" ) ){
        short var;
        if( valStr ){
            sscanf(valStr,"%hd",&var);
            setShort(var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "ulong" ) ){
        unsigned long var;
        if( valStr ){
            sscanf(valStr,"%lu",&var);
            setULong(var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "long" ) ){
        long var;
        if( valStr ){
            sscanf(valStr,"%ld",&var);
            setLong(var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "byte" ) ){
        unsigned short var;
        if( valStr ){
            sscanf(valStr,"%hu",&var);
            setByte((unsigned char)var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "char" ) ){
        char var;
        if( valStr ){
            sscanf(valStr,"%c",&var);
            setChar(var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "flt" ) ){
        float var;
        if( valStr ){
            sscanf(valStr,"%f",&var);
            setFloat(var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "dbl" ) ){
        double var;
        if( valStr ){
            sscanf(valStr,"%f",(float*)&var);
            setDouble(var);
        }else{
            printf("Argument \"valeur\" necessaire pour cette operation\n");
        }
    }
    else if( !strcmp( typeStr, "str" ) ){
        char buffer[1024];
        cin.ignore();
        cin.getline(buffer,1024);
        setString(buffer);
    }
    else{
        clear();
    }
}

string variant::toString(){
    char buffer[80];
    string out;
    switch( byType ){
        case VAR_UINT:      sprintf(buffer,"%u",uVal.uiVal);                    return string(buffer);
        case VAR_INT:       sprintf(buffer,"%d",uVal.iVal);                     return string(buffer);
        case VAR_USHORT:    sprintf(buffer,"%hu",uVal.unVal);                   return string(buffer);
        case VAR_SHORT:     sprintf(buffer,"%hd",uVal.nVal);                    return string(buffer);
        case VAR_ULONG:     sprintf(buffer,"%lu",uVal.ulVal);                   return string(buffer);
        case VAR_LONG:      sprintf(buffer,"%ld",uVal.lVal);                    return string(buffer);
        case VAR_BYTE:      sprintf(buffer,"%hu",(unsigned short)(uVal.byVal)); return string(buffer);
        case VAR_CHAR:      sprintf(buffer,"%c",uVal.cVal);                     return string(buffer);
        case VAR_FLOAT:     sprintf(buffer,"%f",uVal.fVal);                     return string(buffer);
        case VAR_DOUBLE:    sprintf(buffer,"%f",uVal.dVal);                     return string(buffer);
        case VAR_STRING:    out = uVal.pszVal;                           return out;
        case VAR_DATA:      out = "BIN";                                 return out;
        default:            out = "";                                    return out;
    }
}
